require "highline"

require "wondercap/lib/multi_configuration/multi_configuration"
require "wondercap/lib/multi_configuration/default_area_finder"
require "wondercap/lib/multi_configuration/inner_configuration_mixin"
require "wondercap/lib/multi_configuration/outer_configuration_mixin"
require "wondercap/lib/utils/pid_file"
require "wondercap/lib/utils/ui_table"

class << self
  include Wondercap::OuterConfigurationMixin
  
  def area(name = nil, &block)
    name ? configuration(name, &block) : fetch(:area)
  end
end

$wcap = self

load "wondercap/lib/multi_configuration/recipes/names"

namespace :wondercap do
  set :area_name, fetch(:area, nil)
  set :area, nil
  
  set :remote, fetch(:remote, !fetch(:area_name, nil).nil?)
  set :remote, false if area_name == "local"

  task :init_highline do
    set :ui, HighLine.new.extend(Wondercap::UI::TableMixin)
  end
  
  task :init_multi_configuration do
    stderr_logger = Capistrano::Logger.new(:output => StdErrLoggerDevice.new)
    stderr_logger.level = fetch(:verbose, Capistrano::Logger::INFO).to_i
    logger.nested_loggers << stderr_logger

    configuration(:base) do
      set :tags, []
    end

    area :local do
      role :default, "localhost"
    end
    
    set :mc_search_paths, []
  end
  
  after "wondercap:init_multi_configuration" do
    unless fetch(:no_pid_file, false)
      set :pid_file, Wondercap::PidFile.new("/var/tmp/wondercap.pid")
      unless pid_file.lock_nb
        raise "detected another instance of wondercap by checking #{pid_file.to_s}"
      end
      at_exit { pid_file.unlock_nb }
    end
  end

  
  task :fill_search_paths do
    mc_search_paths << "/usr/local/wondercap/extensions/" << "/etc/wondercap.rb" << "/etc/wondercap.mcap" << "/usr/local/etc/wondercap.rb" << "/usr/local/etc/wondercap.mcap" << "/usr/local/wondercap/conf/"
  end
  
  task :process_multi_configuration_files do
    logger.trace "using search paths: #{mc_search_paths.join("\n\t")}"
    mc_search_paths.each do |sp|
      next unless File.exist? sp
      if File.directory? sp
        Dir[File.join(sp, "**", "*.{rb,mcap}")].each { |fin| logger.trace "loading: #{fin}"; top.load fin }
      else
        logger.trace "loading: #{sp}"
        top.load sp
      end
    end
  end
  
  after "wondercap:process_multi_configuration_files", "wondercap:init_additional_loggers"
  task :init_additional_loggers do
    if fetch(:enable_file_logger, true)
      FileUtils.mkdir_p "/usr/local/wondercap/log" unless exists? :log_file_path
      # NOTE: there is a possible caveat here. if multiple users use Wondercap, the log file will be first created
      # with executing user's permissions (mode 0600). Wondercap won't be able to write to this file when executed
      # by another user. To avoid this, you can create the log file by hand and adjust needed permissions by yourself.
      file_logger = Capistrano::Logger.new(:output => FileLoggerDevice.new(fetch(:log_file_path, "/usr/local/wondercap/log/wondercap.log")))
      file_logger.level = fetch(:file_logger_level, Capistrano::Logger::DEBUG).to_i
      
      logger.nested_loggers << file_logger
    end
  end
    
  task "apply_area" do
    if remote
      area_name = fetch(:area_name, nil) || Wondercap::DefaultAreaFinder.find_default_area(names.map { |name| configuration_clone(name) })
      raise "area name not specified and there's no default" unless area_name
      area_name = area_name.to_s
    else
      area_name = "local"
    end
    
    raise "area '#{area_name}' is undefined" unless configuration_defined? area_name
    set :area_name, area_name

    configuration(:base) do
      act_as area_name

      tags << default_area.to_sym if exists? :default_area
      tags.concat(areas) if exists? :areas        
    end
  end
  after "wondercap:process_multi_configuration_files", "wondercap:apply_area"
  
  on :start do
    init_highline
    init_multi_configuration
    fill_search_paths
    process_multi_configuration_files
  end
  
  desc "List all available configurations"
  task :list_configurations do
    ui.say ui.list(mc.map { |k,v| k.to_s }, :rows)
  end

end
